package com.exercise.calculatorapp.util;

/**
 * 表达式解析器类
 */
public class ExpressionParser {
    private int pos = -1; // 当前解析位置
    private int ch; // 当前字符
    private final String expression; // 待解析的表达式字符串

    /**
     * 构造函数，初始化解析器。
     *
     * @param expression 待解析的数学表达式字符串
     */
    public ExpressionParser(String expression) {
        this.expression = expression;
    }

    /**
     * 移动到下一个字符，并更新当前字符。
     */
    private void nextChar() {
        ch = (++pos < expression.length()) ? expression.charAt(pos) : -1; // 如果到达字符串末尾，ch 设置为 -1
    }

    /**
     * 检查当前字符是否为指定字符，如果是，则跳过该字符。
     *
     * @param charToEat 需要匹配的字符
     * @return 如果匹配成功，返回 true；否则返回 false
     */
    private boolean eat(int charToEat) {
        while (ch == ' ') nextChar(); // 跳过空格
        if (ch == charToEat) {
            nextChar(); // 跳过匹配的字符
            return true;
        }
        return false;
    }

    /**
     * 解析整个表达式。
     * 从最高优先级的因子开始，逐步解析表达式。
     *
     * @return 解析后的表达式结果
     */
    public double parse() {
        nextChar(); // 初始化解析位置
        double x = parseExpression(); // 开始解析表达式
        if (pos < expression.length()) { // 如果解析未完成，抛出异常
            throw new RuntimeException("Unexpected: " + (char) ch);
        }
        return x; // 返回最终结果
    }

    /**
     * 解析表达式，处理加法和减法运算。
     * 表达式由多个项组成，通过递归解析项来实现。
     *
     * @return 解析后的表达式结果
     */
    private double parseExpression() {
        double x = parseTerm(); // 解析第一个项
        for (; ; ) {
            if (eat('+')) x += parseTerm(); // 如果遇到加号，累加下一个项
            else if (eat('-')) x -= parseTerm(); // 如果遇到减号，减去下一个项
            else return x; // 如果没有加减号，返回当前结果
        }
    }

    /**
     * 解析项，处理乘法和除法运算。
     * 项由多个因子组成，通过递归解析因子来实现。
     *
     * @return 解析后的项结果
     */
    private double parseTerm() {
        double x = parseFactor(); // 解析第一个因子
        for (; ; ) {
            if (eat('*')) x *= parseFactor(); // 如果遇到乘号，乘以下一个因子
            else if (eat('/')) x /= parseFactor(); // 如果遇到除号，除以下一个因子
            else return x; // 如果没有乘除号，返回当前结果
        }
    }

    /**
     * 解析因子，处理括号和数字。
     * 因子可以是括号内的表达式或直接的数字。
     *
     * @return 解析后的因子结果
     */
    private double parseFactor() {
        if (eat('+')) return parseFactor(); // 如果遇到正号，忽略并解析下一个因子
        if (eat('-')) return -parseFactor(); // 如果遇到负号，取反并解析下一个因子

        double x;
        int startPos = this.pos; // 记录因子的起始位置
        if (eat('(')) { // 如果遇到左括号，递归解析括号内的表达式
            x = parseExpression();
            eat(')'); // 确保遇到右括号
        } else {
            if ((ch < '0' || ch > '9') && ch != '.') { // 如果当前字符不是数字或小数点，抛出异常
                throw new RuntimeException("Unexpected: " + (char) ch);
            }
            while ((ch >= '0' && ch <= '9') || ch == '.') nextChar(); // 读取完整的数字或小数
            String substr = expression.substring(startPos, this.pos); // 提取数字字符串
            x = Double.parseDouble(substr); // 将字符串转换为数字
        }
        return x; // 返回因子结果
    }
}
